package com.bblocks.common.anti_reptile.rule.impl;


import com.bblocks.common.anti_reptile.config.AntiReptileProperties;
import com.bblocks.common.anti_reptile.config.bean.IpRule;
import com.bblocks.common.anti_reptile.config.bean.RuleComm;
import com.bblocks.common.anti_reptile.constant.RuleResultEnum;
import com.bblocks.common.anti_reptile.constant.RuleTypeEnum;
import com.bblocks.common.anti_reptile.rateLimiter.RateLimiterPlan;
import com.bblocks.common.anti_reptile.rule.AbstractRule;
import com.bblocks.common.anti_reptile.service.TokenService;
import com.bblocks.common.anti_reptile.util.ServletUtils;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

/**
 * <p>Project: bb-anti-reptile - IpPlusRule</p>
 * <p>描述：基于ip的复杂规则（仅限未登录）,以此如下(未匹配则后续规则)
 * 1、基于统计规则
 * 2、默认规则（requestMaxSize / expirationTime）
 * 3、直接放行
 * </p>
 *
 * @Author Zhao [125043150@qq.com]
 * @Date 2024/3/23 14:40
 * @Version 1.0
 * @since 8
 */
@Slf4j
public class IpPlusRule extends AbstractRule {

    private final RuleTypeEnum ruleType = RuleTypeEnum.IP_PLUS;
    private AntiReptileProperties properties;
    private RateLimiterPlan rateLimiterPlan;
    private TokenService tokenService;

    public IpPlusRule(RateLimiterPlan rateLimiterPlan,AntiReptileProperties properties,TokenService tokenService) {
        this.rateLimiterPlan = rateLimiterPlan;
        this.properties = properties;
        this.tokenService = tokenService;
    }

    @Override
    @SuppressWarnings("unchecked")
    protected RuleComm doMatch(HttpServletRequest request, HttpServletResponse response) {
        String ipAddress = ServletUtils.getIP(request);
        if(properties.getIpPlusRule().isIgnoreIp(ipAddress)){
            return null;
        }
        if(userId(request,properties.getIpPlusRule(),tokenService) != null){//已登录状态
            return null;
        }

        String requestUrl = ServletUtils.getUrlWithoutParam(request);
        //毫秒，默认5000
        int expirationTime = properties.getIpPlusRule().getExpirationTime();
        //锁定时间-秒
        long lockTime = properties.getIpPlusRule().getLockExpire();
        //最高expirationTime时间内请求数
        int requestMaxSize = -1;

        RuleResultEnum resultEnum = RuleResultEnum.getRuleResult(properties.getIpPlusRule().getLockType());
        if(properties.getIpPlusRule().isBlackIp(ipAddress)){
            log.info("Intercepted request, uri: {}, ip：{}。Remarked by config!", requestUrl, ipAddress);
            return properties.getIpPlusRule();
        }
        if (rateLimiterPlan.isBlack(ruleType, ipAddress)) {
                log.info("Intercepted request, uri: {}, ip：{}。Automatically unlock after {} seconds", requestUrl, ipAddress
                        , lockTime);
                return properties.getIpPlusRule();
        }


       if(!rateLimiterPlan.tryAcquire(ruleType,ipAddress,requestUrl, requestMaxSize, expirationTime, TimeUnit.MILLISECONDS)) {
            if(resultEnum.isNeedBlack()) {
                rateLimiterPlan.joinBlack(properties.getIpPlusRule(), ipAddress, requestUrl, lockTime, TimeUnit.SECONDS);
                if (log.isInfoEnabled())
                    log.info("Intercepted request, uri: {}, ip：{}, request :{}, times in {} ms。Automatically unlock after {} seconds", requestUrl, ipAddress, requestMaxSize, expirationTime, lockTime);
            }
            return properties.getIpPlusRule();
        }


        return null;
    }

    /**
     * 重置已记录规则
     * @param request 请求
     * @param realRequestUri 原始请求uri
     */
    @Override
    public void reset(HttpServletRequest request, String realRequestUri) {
        String ipAddress = ServletUtils.getIP(request);
        /**
         * 重置计数器,以及清除黑名单
         */
       int ret = this.resetDefault(properties.getIpPlusRule(),rateLimiterPlan,ipAddress);
       log.info("解除结果：{}",ret);
    }


    @Override
    public int getOrder() {
        return 3;
    }
}
